Patterns
1. Two Pointers
[[Two Pointer]]
Description: This method uses two pointers to traverse an array or a list from different ends or directions.
- Two Pointers is a pattern where two pointers iterate through the data structure in tandem until one or both of the pointers hit a certain condition
- Two Pointers is often useful when searching pairs in a sorted array or linked list
- Two Pointers are needed because with just pointer, you would have to continually loop back through the array to find the answer, this back and forth with a single iterator is inefficient for time and space complexity
Usage: Particularly useful for ordered data structures, where we can make intelligent decisions based on the position of the pointers.
- Some ways to identify that the given problem might require a two pointer
- It will feature problems where you deal with sorted array (or Linked List) and need to find a set of elements that fufill certain conditions
- The set of elements in the array is a pair, triple, or subarray
- Common problems you use the sliding two pointers with
- Squaring a sorted array
- Triples that sum to zero
- Comparing strings that contains backspaces
Example Problems:
- Pair with Target Sum
- Remove Duplicates
- Squaring a Sorted Array
2. Island (Matrix Traversal)
Description: Involves traversing a matrix to find 'islands' or contiguous groups of elements.
Usage: Generally used in grid-based problems, especially when we need to group connected elements together.
Example Problems:
- Number of Islands
- Max Area of Island
- Flood Fill
3. Fast & Slow Pointers
Description: In this method, two pointers move at different speeds in a data structure.
- Also known as the Hare & Tortoise algorithm, is a pointer algorithm that uses two pointers which move through the array (or sequence/linked list) at different speeds. This approach is quite useful when dealing with cyclic linked or arrays
- By moving at different speeds, the algorithm proves that the two pointers are bound to meet. The fast pointer should catch the slow pointer once both the pointers are in a cyclic loop.
Usage: Commonly used to detect cycles in a structure, find middle elements, or to solve other specific problems related to linked lists.
- Some ways to identify that the given problem might require a Fast and Slow pattern
- The problem will deal with a loop in a linked list or array
- When you need to know the position of a certain element or the overall length of the linked list
- When should I use it over the [[1. Overview#1. Two Pointers | Two Pointers pattern]]:
- There are some cases where you shouldn't use the Two Pointer approach such as in a singly linked list where you can't move in a backwards direction. An example of when to use the Fast and Slow pattern is when you're trying to determine if a linked list is a palindrome
Example Problems:
- LinkedList Cycle
- Middle of the LinkedList
- Palindrome LinkedList
More information: [[Linked List#Linked List Two Pointer]]
4. Sliding Window
[[Sliding Window]] Description: This pattern involves creating a 'window' into the data structure and then moving that window around to gather specific information.
- Used to perform a required operation on a specific window size of a given array or linked list, such as finding the longest subarray containing all 1s
- In some cases, the window size remains constant and in other cases the size grows or shrinks
Usage: Mostly used in array or list-based problems where you need to find a contiguous subset that fulfills certain conditions.
- Some ways to identify that the given problem might require a sliding window
- The problem input is a linear data structures such as linked list, array, or string
- You're asked to find the longest/shortest substring, subarray or a desired value
Example Problems:
- Maximum Sum Subarray of Size K
- Smallest Subarray with a given sum
- Longest Substring with K Distinct Characters
5. Merge Intervals
Description: This pattern involves merging overlapping intervals.
Usage: Often used in problems involving time intervals, ranges, or sequences.
Example Problems:
- Merge Intervals
- Insert Interval
- Intervals Intersection
6. Cyclic Sort
Description: This pattern involves sorting an array containing numbers in a given range.
Usage: Useful in situations where the data involves a finite range of natural numbers.
Example Problems:
- Cyclic Sort
- Find the Missing Number
- Find all Duplicates
7. In-place Reversal of a Linked List
Description: This pattern involves reversing elements of a linked list in-place.
Usage: Generally used when reversing a sequence without using extra space.
Example Problems:
- Reverse a LinkedList
- Reverse a Sub-list
- Reverse Every K-element Sub-list
8. Tree Breadth First Search
[[Tree]] Description: This pattern involves level-by-level traversal of a tree.
- This pattern is based on the Breadth First Search (BFS) technique to traverse a tree and uses a queue to keep track of all the nodes of a level before jumping onto the next level.
- Any problem involving the traversal of a tree in a level-by-level order can be efficiently solved using this approach
- The Tree BFS pattern works by pushing the root node to the queue and then continually iterating until the queue is empty. For each iteration, we remove the node at the head of the queue and "visit" that node. After removing each node from the queue, we also insert all of its children into the queue.
Usage: Used when we need to traverse a tree or graph in a level-by-level (breadth-first) manner.
- Some ways to identify that the given problem might require a Tree BFS pattern
- If you' asked to traverse a tree in a level-by-level fashion (or level order traversal)
Example Problems:
- Level Order Traversal
- Reverse Level Order Traversal
- Zigzag Traversal
9. Tree Depth First Search
[[Tree]] Description: This pattern involves traversing a tree or graph depth-wise before visiting siblings or neighbors.
- Tree DFS is based on the Depth First Search (DFS) technique to traverse a tree.
- You can use recursion (or a stack for the iterative approach) to keep track of all the previous (parent) nodes while traversing
- The Tree DFS pattern works by starting at the root of the tree, if the node is not a leaf you need to do three things:
- Decide whether to process the current node now (pre-order), or between processing two children (in-order) or after processing both children (post-order)
- Make two recursive calls for both the children of the current node to process them
Usage: Used when you need to search deeper into a tree/graph first before going across.
- Some ways to identify that the given problem might require a Tree DFS pattern
- If you're asked to traverse a tree with in-order, preorder, or postorder DFS
- If the problem requires searching for something where the node is closer to a leaf
Example Problems:
- Binary Tree Path Sum
- All Paths for a Sum
- Count Paths for a Sum
10. Two Heaps
[[Heap#Two Heaps Pattern]] Description: This pattern involves using two heaps to divide a set of numbers into two parts.
- In many problems, we are given a set of elements such that we can divide them into two parts. To solve the problem we are interested in knowing the smallest element in one part and the biggest element in the other part. This pattern is an efficient approach to solve such problem.
Usage: Useful when you need to find median numbers in a sequence, or other similar problems.
- Ways to identify the Two Heaps pattern:
- Useful in situations like Priority Queue, Scheduling
- If the problem states that you need to find the smallest/largest/median elements of a set
- Sometimes, useful in problems featuring a binary tree data structures
Example Problems:
- Find the Median of a Number Stream
- Sliding Window Median
- Maximize Capital
11. Subsets
Description: This pattern involves generating all subsets of a set.
- A huge number of coding interview problems involve dealing with Permutation and Combination of a given set of elements. The pattern usually uses [[Backtracking]] to solve these kind of problems.
Usage: Helpful for solving problems that require exploring all subsets of a given set.
Example Problems:
- Subsets
- Subsets With Duplicates
- Permutations
12. Modified Binary Search
[[Binary Search]] Description: This is a tweaked version of the binary search algorithm.
- Whenever you are given a sorted array, linked list, or matrix, and are asked to find a certain element, the best algorithm you can use is Binary Search
Usage: Used when a simple binary search isn't sufficient, like finding a number in a bitonic array.
Example Problems:
- Order-agnostic Binary Search
- Ceiling of a Number
- Next Letter
13. Top 'K' Elements
[[Heap]] Description: This pattern is used to find the top 'k' elements among a certain category.
Any problem that asks us to find the top/smallest/frequent "K" elements among a given set falls under this pattern
The best data structure to keep track of "K" elements is Heap. This pattern will make use of the Heap to solve multiple problems dealing with "K" elements at a time from a set of given elements.
The pattern looks like this
- We would need a way to continuously keep track of a (or many) smallest/largest element => Use a heap
- Insert "K" elements into the min-heap or max-heap based on the problem
- Iterate through the remaining numbers and if you find one that is larger than what you have in the heap, then remove that number and insert the larger one or decide what to do with the largest/smallest element (pop, replace, etc)
Usage: Commonly used in problems involving sorting, searching, and in heap data structures.
- Some ways to identify the Top K element
- If you're asked to find the top/smallest/frequent K elements of a given set
- If you're asked to sort an array to find an exact element
Example Problems:
- Top K Frequent Numbers
- Kth Largest Number in a Stream
- Top K Frequent Elements
14. Bitwise XOR
Description: This pattern involves the use of Bitwise XOR to solve various array-based problems.
Usage: Used when we need to manipulate and compare bits directly.
Example Problems:
- Single Number
- Two Single Numbers
- Complement of Base 10 Number
15. Backtracking
[[Backtracking]] Description: This pattern involves exploring all possible solutions and then backtracking to correct the course whenever you're on the wrong path.
Usage: Typically used for solving complex combinatorial problems, puzzles, and games.
Example Problems:
- Sudoku Solver
- N-Queens
- Generate Parentheses
16. 0/1 Knapsack (Dynamic Programming)
[[DP]] [[Knapsack]] Description: This pattern deals with problems where items have different values and weights, and we need to determine the maximum value we can carry.
Usage: Typically used in optimization problems, especially those involving physical constraints.
Example Problems:
- 0/1 Knapsack
- Equal Subset Sum Partition
- Subset Sum
17. Topological Sort (Graph)
[[Graph#Topological Sort | Topological Sort]] Description: This pattern involves sorting nodes in a directed graph in a specific order where the preceding node comes before the following node.
- Topological Sort is used to find a linear ordering of elements that have dependencies on each other (event B is dependent on event A, A comes before B in topological ordering)
Usage: Used for scheduling problems and in scenarios where order needs to be imposed on how you process nodes.
- Some ways to identify that the given problem might require a Topological Sort pattern
- The problem will deal with graphs that have no directed cycles
- If you're asked to update all objects in a sorted order
- If you have a class of objects that follow a particular order
Example Problems:
- Task Scheduling Order
- All Tasks Scheduling Orders
- Alien Dictionary
18. K-way Merge
Description: This pattern involves merging 'k' sorted lists.
Usage: Typically used in problems involving lists, where merging is required.
Example Problems:
- Merge K Sorted Lists
- Kth Smallest Number in M Sorted Lists
- Smallest Number Range
19. Monotonic Stack
[[Stack#Monotonic Stack | Monotonic Stack]] Description: This pattern involves using a stack to maintain a monotonic (either entirely non-increasing or non-decreasing) order of elements.
Usage: Often used for solving problems where you need to find the next greater or smaller elements.
Example Problems:
- Next Greater Element
- Next Smaller Element
- Largest Rectangle in Histogram
20. Multi-threaded
Description: This pattern involves designing algorithms that can execute multiple threads in parallel.
Usage: Used in situations where a task can be divided into independent sub-tasks that can execute concurrently.
Example Problems:
- Invert Binary Tree
- Binary Search Tree Iterator
- Same Tree
21. Union Find
[[Union Find]] Description: Union Find, also known as Disjoint Set Union (DSU), is a data structure that keeps track of a partition of a set into disjoint subsets.
Usage: Particularly useful for problems where we need to find whether 2 elements belong to the same group or need to solve connectivity-related problems in a graph or tree.
Example Problems:
- Graph Redundant Connection
- Number of Provinces
- Is Graph Bipartite
Algorithms
These are helpful algorithms that usually comes up in interview, these list may share some overlaps to the [[1. Overview#Patterns | Patterns section]] but some maybe to niche to be called a patterns, that's why I split it out to this section.
Binary Search [[Binary Search]]
- Used for problems involving sorted arrays, search ranges, or optimization
- Example problems:
- Find peak element
- Binary Search
- Search in Rotated Sorted Array
Merge Sort and Quick Sort
- Understanding sorting algorithms for custom sorting problems
Two-Pointer Technique [[Two Pointer]]
- Used for sorted arrays, finding pairs or triplets that meet a condition
- Example problems:
Sliding Window [[Sliding Window]]
- Common in problems involving subarrays or substrings
- Example problems:
Activity Selection
- Used for interval scheduling problems
- Example problems:
Huffman Encoding
- Useful for compression-related problems
Kruskal's and Prim's Algorithm
- Finding Minimum Spanning Trees (MST)
- Example problems:
Knapsack Variants [[DP]] and [[Knapsack]]
- Classic DP problem (0/1 Knapsack, Unbounded Knapsack)
- Example problems:
Longest Common Subsequence (LCS) [[DP#DP on String]]
- Example problems:
Matrix-based Dynamic Programming
- Pathfinding problems
- Example problems:
Breadth-First Search (BFS) [[Graph]] and [[Tree]]
- For shortest path and level order traversal
- Example problems:
Depth-First Search (DFS) [[Graph#DFS]] and [[Tree]]
- For traversals, connected components, backtracking
Dijkstra's Algorithm [[Graph#Dijkstra's Shortest Path]]
- Shortest path in weighted graphs
- Example problems:
Union-Find (Disjoint Set Union) [[Union Find]]
- For connected components and cycle detection
- Example problems:
Topological Sorting [[Graph#Topological Sort]]
- Used in dependency problems
- Example problems:
Binary Tree Traversals [[Tree]]
- Inorder, Preorder, Postorder
- Example problems:
Lowest Common Ancestor (LCA) [[Tree#Lowest Common Ancestor]]
- Common in tree-related questions
- Example problems:
Trie (Prefix Tree)
- Common in string matching and autocomplete problems
- Example problems:
KMP Algorithm
- For pattern matching
- Example problems:
Manacher's Algorithm
- Finding longest palindromic substring
- Example problems:
Greatest Common Divisor (GCD)
- Euclid's Algorithm for GCD
- Example problems:
Sieve of Eratosthenes
- For prime number generation
- Example problems:
Backtracking - Permutations and Combinations [[Backtracking]]
- Generate all possibilities
- Example problems:
Heap / Priority Queue [[Heap]]
- For k-th largest/smallest element problems
- Example problems:
Bit Manipulation
- Basic operations (AND, OR, XOR, shifts)
- Subset generation using binary representation
- Single number problems using XOR
- Bitmask DP for state handling
Monotonic Stack [[Stack#Monotonic Stack]]
- Example: Largest Rectangle in Histogram
Prefix Sum and Difference Arrays [[Prefix Sum]] and [[Special Techniques#Difference Array]]
- Example: Range Sum Query
- Example: Product of Array Except Self
Miscellaneous Techniques
- Reservoir Sampling
- Example: Linked List Random Node